#include "iluminacao.hpp"
Iluminacao::Iluminacao(void) { }

Iluminacao::~Iluminacao(void) { }

Cor Iluminacao::calculaCorRec(const int objeto, const Raio& raio, Vetor ponto, Cena* cena, const unsigned short profundidade) const {
	Cor cor = calculaCor(objeto,raio,ponto,cena);
	Cor cor_;
	if(profundidade == 0) return cor;
	
	Raio raio_ = raioRefletido(raio, ponto, cena->getObjeto(objeto)->getNormal(ponto));
	Vetor ponto_;
	int objeto_ = cena->objetoProximo(ponto_,raio_);
	if (objeto_ != -1) {	 
		cor_ = calculaCorRec(objeto_,raio_,ponto_,cena, profundidade - 1);

	}
	cor.set(cor.getR()+cor_.getR(),cor.getG()+cor_.getG(),cor.getB()+cor_.getB());
	return cor;
}
Cor Iluminacao::calculaCor(const int indice,const Raio& r,const Vetor& ponto,Cena* cena) const {
	const double atenuacao = cena->getAtenuacao(ponto.subVet(r.getOrigem()).modulo());
	
	Cor cor;
	double difusa = 0.0,especular = 0.0;
	/*Pegando objeto e Material*/
	Objeto* objeto = cena->getObjeto(indice);
	Material* material = objeto->getMaterial();
		
	/*Recebendo valores do material*/
	const Cor matAmb = material->getAmbiente();
	const Cor matDif = material->getDifusa();
	const Cor matEsp = material->getEspecular();
	const double brilho = material->getBrilho();

	/* Ambiente*/
	double ambiente = 0.3; 
	Vetor normal = objeto->getNormal(ponto);
	
	for(int l=0;l<cena->getLuzesEnd();l++) { /*Para cada luz, calcular qual contribuicao difusa e especular ira ficar no ponto */
		Luz* luz = cena->getLuz(l);
		
		const Vetor L = (luz->getOrigem().subVet(ponto)).normaliza();
	
		/* Difusa */
		difusa = calcDifusa(L,normal);
								
		/*  Especular */
		especular = calcEspecular(L,normal,r);
		
		difusa = max(0.0,difusa);
		especular = pow(max(0.0,especular),brilho);
		
		/*Recebendo valores da luz*/
		const Cor luzAmb = luz->getAmbiente();
		const Cor luzDif = luz->getDifusa();
		const Cor luzEsp = luz->getEspecular();

		cor.setR(cor.getR() +
			atenuacao*(
			luzEsp.getR()*matEsp.getR()*especular+
			luzDif.getR()*matDif.getR()*difusa+
			luzAmb.getR()*matAmb.getR()*ambiente
					  ));
		cor.setG(cor.getG() +
			atenuacao*(
			luzEsp.getG()*matEsp.getG()*especular+
			luzDif.getG()*matDif.getG()*difusa+
			luzAmb.getG()*matAmb.getG()*ambiente
					  ));
		cor.setB(cor.getB() +
			atenuacao*(
			luzEsp.getB()*matEsp.getB()*especular+
			luzDif.getB()*matDif.getB()*difusa+
			luzAmb.getB()*matAmb.getB()*ambiente
					  ));
	}
	return cor;
}

Raio Iluminacao::raioRefletido(const Raio& r,Vetor ponto,Vetor normal) const{ 
	const Vetor offset = Vetor(normal.x*0.001,normal.y*0.001,normal.z*0.001);
	const Vetor incidente = r.getDirecao().multEsc(-1.0);
	const Vetor reflexao = incidente.calcReflex(normal.normaliza());
	ponto.x = ponto.x+offset.x;
	ponto.y = ponto.y+offset.y;
	ponto.z = ponto.z+offset.z;
	return Raio(ponto,reflexao);
}
double Iluminacao::calcDifusa(const Vetor& L, const Vetor& normal) const {
	return L.prodEsc(normal);	
}

double Iluminacao::calcEspecular(const Vetor& L,const Vetor& normal,const Raio& r) const {

	Vetor reflexao = L.calcReflex(normal);
	return (r.getDirecao().multEsc(-1.0)).prodEsc(reflexao);
	
}

